package com.example.study.jetpack.compose

import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.AlignmentLine
import androidx.compose.ui.layout.FirstBaseline
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layout
import androidx.compose.ui.unit.Dp

/**
 *  created by ws
 *   on 2022/2/28
 *   describe:https://blog.csdn.net/Mr_Tony/article/details/118765859?spm=1001.2014.3001.5502
 */
class Compose中的自定义布局 {
/*可以使用layout修饰符来元素的测量和布局方式
* fun Modifier.customLayoutModifier(...) = Modifier.layout { measurable, constraints ->
  ...
})
* 不过实际通常以下写法
* fun Modifier.customLayoutModifier(...) =
    this.layout { measurable, constraints ->
        ...
    })
如果想控制text顶部到第一行基线的位置
*/
fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // 检查是否包含基线，如果不包含则会引发异常
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}
/*创建自定义布局
* layout修饰符仅更爱调用可组合项,如需测量和布置多个可组合项,应改用Layout可组合项.
* 此组合项允许你手动测量和布置子项.Column和Row等所有较高级别的布局都使用Layout可组合
* 项构建而成*/
@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}
/*固有特性测量
* 一般来说,在自定义布局中使用默认测量方式就可以了,但是有时候可能并不能满足需求.
* 因此要指定自定义Layout的固有特性测量,则在创建该布局时替换MeasurePolicy的
* minIntrinsicWidth,minIntrinsicHeight,maxIntrinsicWidth和maxIntrinsicHeight
* https://blog.csdn.net/Mr_Tony/article/details/118765859?spm=1001.2014.3001.5502
* */

/*分析Modifier.padding的原理
* // How to create a modifier
@Stable
fun Modifier.padding(all: Dp) =
    this.then(
        PaddingModifier(start = all, top = all, end = all, bottom = all, rtlAware = true)
    )

// Implementation detail
private class PaddingModifier(
    val start: Dp = 0.dp,
    val top: Dp = 0.dp,
    val end: Dp = 0.dp,
    val bottom: Dp = 0.dp,
    val rtlAware: Boolean,
) : LayoutModifier {

    override fun MeasureScope.measure(
        measurable: Measurable,
        constraints: Constraints
    ): MeasureResult {

        val horizontal = start.roundToPx() + end.roundToPx() //获取padding的横向长度
        val vertical = top.roundToPx() + bottom.roundToPx() //获取padding的垂直长度

        // val placeable = measurable.measure(constraints.offset(horizontal, vertical)) //偏移horizontal、vertical距离后进行测量，偏移只会更改内容位置，不会影响测量大小，因为下面已经进行偏移了，所以可以不用这么麻烦
 		val placeable = measurable.measure(constraints)
        val width = constraints.constrainWidth(placeable.width + horizontal)
        val height = constraints.constrainHeight(placeable.height + vertical)
        return layout(width, height) {//定义父布局宽高
            if (rtlAware) {
                placeable.placeRelative(start.roundToPx(), top.roundToPx()) //将组件在现有位置上进行移动，该移动是在布局里面，所以并不会超出布局宽高
            } else {
                placeable.place(start.roundToPx(), top.roundToPx())
            }
        }
    }
}

* 这里有个有意思的问题，就是调用placeable。placeRelative便宜后为什么不会
* 超出设置的宽高。
* Placeable，文档上解释的意思是：Placeable对应于可以由其父布局定位的子布局。
* 大多数Placeable是Measurable。measure调用的结果。Placeable。height是父布局所需要留出的高度。
* 而placeable。measuredWidth才是控件真正的测量宽度，因此placeable。placeRelative函数
* 并不会导致组件超出布局*/


}